﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ControleRepresentante.Logic.Models;
using ControleRepresentante.Logic.Statistics;
using NHibernate;
using NHibernate.Criterion;
using Attribute = ControleRepresentante.Logic.Models.Attribute;

namespace ControleRepresentante.Logic.Repositories
{
    public interface IVendedorRepository : IUsuariosHolderRepository<Vendedor, StatisticsBase>
    {
        int GetCount();
        IEnumerable<Vendedor> GetModels(string nome, string cidade, string bairro, Dictionary<Attribute, string> attributeMap, out StatisticsBase statistics, int page = -1);
    }

    public enum VendedorErrors
    {
        DuplicatedNome,
        RepresentanteMissmatchError
    }

    public class VendedorRepository : UsuariosHolderRepository<Vendedor, StatisticsBase, VendedorErrors>, IVendedorRepository
    {
        public VendedorRepository(IRepositoryConfiguration repositoryConfiguration, ISession session) 
            : base(repositoryConfiguration, session)
        {
        }

        protected override void SetProperties(IEnumerable<Vendedor> vendedores)
        {
            base.SetProperties(vendedores);
            if (vendedores.Any())
            {
                string vendedorIds = String.Join(",", vendedores.Select(x => "'" + x.Id.ToString() + "'"));
                
                Dictionary<Guid, int> totalClientes = Session.CreateSQLQuery(String.Format(@"
                    SELECT COUNT(*) AS Total, VendedorId
                    FROM Cliente
                    WHERE VendedorId IN ({0})
                    GROUP BY VendedorId", vendedorIds))
                    .AddScalar("Total", NHibernateUtil.Int32)
                    .AddScalar("VendedorId", NHibernateUtil.Guid)
                    .List()
                    .Cast<object[]>()
                    .ToDictionary(x => (Guid)x[1], x => (int)x[0]);

                foreach (Vendedor vendedor in vendedores)
                {
                    int numeroDeClientes;
                    vendedor.NumeroDeClientes = totalClientes.TryGetValue(vendedor.Id, out numeroDeClientes) ? numeroDeClientes : 0;
                }
            }
        }

        protected override StatisticsBase GetStatistics(int modelCount)
        {
            return new StatisticsBase(modelCount);
        }

        protected override IEnumerable<Expression<Func<Vendedor, bool>>> DoGetUniquenessExpressions(Vendedor model)
        {
            yield return x => x.Nome == model.Nome;
        }

        protected override VendedorErrors RepresentanteMissmatchError
        {
            get { return VendedorErrors.RepresentanteMissmatchError; }
        }

        protected override VendedorErrors DuplicatedError { get { return VendedorErrors.DuplicatedNome; } }

        public int GetCount()
        {
            return Session.QueryOver<Vendedor>()
                .Where(x => x.Representante.Id == GetRepresentanteId())
                .RowCount();
        }

        public IEnumerable<Vendedor> GetModels(string nome, string cidade, string bairro, Dictionary<Attribute, string> attributeMap, out StatisticsBase statistics, int page = -1)
        {
            ICriteria criteria = CreateCriteria(cidade, bairro, attributeMap, "ve");

            if (!String.IsNullOrEmpty(nome))
            {
                string likeNome = "%" + nome + "%";
                criteria = criteria.Add(Restrictions.InsensitiveLike(Projections.Property<Vendedor>(x => x.Nome), likeNome));
            }

            IEnumerable<Vendedor> vendedorFiltro = User.Filtros.OfType<Vendedor>();
            if (vendedorFiltro.Any())
            {
                criteria.Add(Restrictions.In(Projections.Property<Vendedor>(x => x.Id), vendedorFiltro.Select(x => x.Id).ToArray()));
            }

            criteria = criteria.AddOrder(Order.Asc(Projections.Property<Vendedor>(x => x.Nome)));

            int modelCount;
            IEnumerable<Vendedor> vendedores = FuturePaging(criteria, page, out modelCount);
            SetProperties(vendedores);
            statistics = GetStatistics(modelCount);
            return vendedores;
        }

        protected override IQueryOver<Vendedor, Vendedor> DoGetModelsQueryOver(IQueryOver<Vendedor, Vendedor> queryOver)
        {
            return queryOver.OrderBy(x => x.Nome).Asc;
        }

        public override Dictionary<Guid, string> GetModelNames()
        {
            return Session.CreateSQLQuery(@"
                SELECT UsuariosHolderId, Nome
                FROM Vendedor
                INNER JOIN AttributeHolder ON UsuariosHolderId = AttributeHolder.Id
                WHERE RepresentanteId = :RepresentanteId
                ORDER BY Nome")
                .AddScalar("UsuariosHolderId", NHibernateUtil.Guid)
                .AddScalar("Nome", NHibernateUtil.String)
                .SetParameter("RepresentanteId", GetRepresentanteId())
                .List()
                .Cast<object[]>()
                .ToDictionary(x => (Guid)x[0], x => (string)x[1]);
        }

        public override Dictionary<Guid, string> GetModelNames(string search)
        {
            return Session.CreateSQLQuery(@"
                SELECT UsuariosHolderId, Nome
                FROM Vendedor
                INNER JOIN AttributeHolder ON UsuariosHolderId = AttributeHolder.Id
                WHERE RepresentanteId = :RepresentanteId AND Nome LIKE :Search
                ORDER BY Nome")
                .AddScalar("UsuariosHolderId", NHibernateUtil.Guid)
                .AddScalar("Nome", NHibernateUtil.String)
                .SetParameter("RepresentanteId", GetRepresentanteId())
                .SetParameter("Search", "%" + search + "%")
                .List()
                .Cast<object[]>()
                .ToDictionary(x => (Guid)x[0], x => (string)x[1]);
        }
    }
}